/***************************************************************************
 * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite        *
 * Copyright (C) 2014 Konloch - Konloch.com / BytecodeViewer.com           *
 *                                                                         *
 * This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

package the.bytecode.club.bytecodeviewer.malwarescanner;

import org.objectweb.asm.tree.*;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.InstructionPrinter;
import the.bytecode.club.bytecodeviewer.malwarescanner.util.SearchableString;

/**
 * The base class for the malware code scanners
 *
 * @author Konloch
 * @since 6/27/2021
 */
public abstract class MalwareCodeScanner implements CodeScanner
{
    private final InstructionPrinter instructionPrinter = new InstructionPrinter(null, null);
    public MalwareScanModule module;

    public abstract void scanFieldString(MalwareScan scan, ClassNode cn, FieldNode field, SearchableString string);

    public abstract void scanMethodString(MalwareScan scan, ClassNode cn, MethodNode method, SearchableString string);

    public abstract void scanMethodInstruction(MalwareScan scan, ClassNode cn, MethodNode method, AbstractInsnNode instruction);

    @Override
    public void scanningClass(MalwareScan scan, ClassNode cn)
    {
        scanFields(scan, cn, cn.fields.toArray(new FieldNode[0]));
        scanMethods(scan, cn, cn.methods.toArray(new MethodNode[0]));
    }

    @Override
    public void scanFields(MalwareScan scan, ClassNode cn, FieldNode[] fields)
    {
        for (FieldNode field : fields)
        {
            Object fieldValue = field.value;

            //scan strings
            if (fieldValue instanceof String)
                scanFieldString(scan, cn, field, new SearchableString((String) fieldValue));

                //scan string array
            else if (fieldValue instanceof String[])
                for (String s : (String[]) fieldValue)
                    scanFieldString(scan, cn, field, new SearchableString(s));
        }
    }

    @Override
    public void scanMethods(MalwareScan scan, ClassNode cn, MethodNode[] methods)
    {
        for (MethodNode method : methods)
        {
            InsnList instructionList = method.instructions;

            //scan each instruction
            for (AbstractInsnNode instruction : instructionList.toArray())
            {
                scanMethodInstruction(scan, cn, method, instruction);

                if (instruction instanceof LdcInsnNode)
                {
                    if (((LdcInsnNode) instruction).cst instanceof String)
                    {
                        final String string = (String) ((LdcInsnNode) instruction).cst;
                        scanMethodString(scan, cn, method, new SearchableString(string));
                    }
                }
            }
        }
    }

    public String fieldToString(ClassNode cn, FieldNode field)
    {
        return cn.name + "." + field.name + "(" + field.desc + ")";
    }

    public String methodToString(ClassNode cn, MethodNode method)
    {
        return cn.name + "." + method.name + "(" + method.desc + ")";
    }

    public String instructionToString(AbstractInsnNode instruction)
    {
        return instructionPrinter.printInstruction(instruction).trim();
    }

    public String header()
    {
        String header = String.format("%30s", (module.getReadableName() + " ->\t"));

        //TODO display the resource container for this specific ClassNode
        if (BytecodeViewer.viewer.showFileInTabTitle.isSelected())
            header += "{fileContainerGoesHere}\t";

        return header;
    }

    public void foundLDC(MalwareScan scan, String ldc, String foundAt)
    {
        scan.sb.append(header()).append(" Found LDC \"").append(ldc).append("\" ").append(foundAt);
    }

    public void foundMethod(MalwareScan scan, String foundAt)
    {
        scan.sb.append(header()).append(" Found Method call to ").append(foundAt);
    }

    public void found(MalwareScan scan, String found)
    {
        scan.sb.append(header()).append(" Found ").append(found);
    }
}
